"""
Video composer for combining visual content with audio tracks.

Handles video composition, audio synchronization, and final rendering.
"""

import asyncio
import os
import tempfile
from pathlib import Path
from typing import List, Dict, Optional, Any, Tuple
import json

from .timeline import VideoTimeline, VideoConfig, TimelineSegment, SegmentType
from .capture import HTMLVideoCapture
from ..audio.processing.mixer import AudioMixer, AudioTrack, MixingConfig
from ..utils.exceptions import MD2PPTError


class VideoComposer:
    """Composes final video from visual content and audio tracks."""
    
    def __init__(self, config: Optional[VideoConfig] = None):
        """Initialize video composer."""
        self.config = config or VideoConfig()
        self.html_capture = HTMLVideoCapture(config)
        self.audio_mixer = AudioMixer()
        self._temp_files: List[str] = []
    
    async def compose_video(self, 
                          html_path: str,
                          timeline: VideoTimeline,
                          output_path: str,
                          background_music: Optional[str] = None) -> str:
        """
        Compose complete video with synchronized audio.
        
        Args:
            html_path: Path to HTML presentation
            timeline: Video timeline with segments
            output_path: Output video file path
            background_music: Optional background music file
            
        Returns:
            Path to composed video file
        """
        try:
            print("🎬 开始视频合成...")
            
            # Step 1: Capture video content
            print("📹 捕获视频内容...")
            video_path = await self._capture_video_content(html_path, timeline)
            
            # Step 2: Prepare audio track
            print("🎵 准备音频轨道...")
            audio_path = await self._prepare_audio_track(timeline, background_music)
            
            # Step 3: Combine video and audio
            print("🎞️ 合成最终视频...")
            final_path = await self._combine_video_audio(video_path, audio_path, output_path)
            
            # Step 4: Add subtitles if needed
            print("📺 添加字幕...")
            final_path = await self._add_subtitles(final_path, timeline, output_path)
            
            print(f"✅ 视频合成完成: {final_path}")
            return final_path
            
        except Exception as e:
            await self._cleanup_temp_files()
            if isinstance(e, VideoCompositionError):
                raise
            else:
                raise VideoCompositionError(f"Video composition failed: {e}")
    
    async def _capture_video_content(self, html_path: str, timeline: VideoTimeline) -> str:
        """Capture video content from HTML presentation."""
        # Get slide segments for capture
        slide_segments = timeline.get_slide_segments()
        
        if not slide_segments:
            raise VideoCompositionError("No slide segments found in timeline")
        
        # Create temporary video path
        video_path = self._create_temp_file("video_content.mp4")
        
        # Capture HTML to video
        await self.html_capture.capture_html_to_video(
            html_path, video_path, slide_segments
        )
        
        return video_path
    
    async def _prepare_audio_track(self, timeline: VideoTimeline, 
                                 background_music: Optional[str] = None) -> str:
        """Prepare synchronized audio track."""
        audio_segments = timeline.get_audio_segments()
        
        if not audio_segments and not background_music:
            # Create silent audio track
            return await self._create_silent_audio(timeline.total_duration)
        
        # Create audio tracks for mixing
        tracks = []
        
        # Add narration tracks
        for segment in audio_segments:
            if segment.audio_path and os.path.exists(segment.audio_path):
                track = AudioTrack(
                    file_path=segment.audio_path,
                    start_time=segment.start_time,
                    duration=segment.duration,
                    volume=1.0,
                    track_type="narration"
                )
                tracks.append(track)
        
        # Add background music if provided
        if background_music and os.path.exists(background_music):
            track = AudioTrack(
                file_path=background_music,
                start_time=0.0,
                duration=timeline.total_duration,
                volume=0.3,  # Lower volume for background
                loop=True,
                fade_in=1.0,
                fade_out=1.0,
                track_type="music"
            )
            tracks.append(track)
        
        # Mix audio tracks
        mixed_audio_path = self._create_temp_file("mixed_audio.mp3")
        
        if tracks:
            await self.audio_mixer.mix_tracks(tracks, mixed_audio_path, timeline.total_duration)
        else:
            # Create silent track if no audio
            mixed_audio_path = await self._create_silent_audio(timeline.total_duration)
        
        return mixed_audio_path
    
    async def _create_silent_audio(self, duration: float) -> str:
        """Create silent audio track for given duration."""
        silent_path = self._create_temp_file("silent_audio.mp3")
        
        cmd = [
            "ffmpeg", "-y",
            "-f", "lavfi",
            "-i", f"anullsrc=channel_layout=stereo:sample_rate=44100",
            "-t", str(duration),
            "-c:a", "libmp3lame",
            silent_path
        ]
        
        process = await asyncio.create_subprocess_exec(
            *cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        
        stdout, stderr = await process.communicate()
        
        if process.returncode != 0:
            error_msg = stderr.decode() if stderr else "Unknown FFmpeg error"
            raise VideoCompositionError(f"Silent audio creation failed: {error_msg}")
        
        return silent_path
    
    async def _combine_video_audio(self, video_path: str, audio_path: str, output_path: str) -> str:
        """Combine video and audio into final video file."""
        temp_output = self._create_temp_file("combined_video.mp4")
        
        cmd = [
            "ffmpeg", "-y",
            "-i", video_path,
            "-i", audio_path,
            "-c:v", "copy",  # Don't re-encode video
            "-c:a", self.config.audio_codec,
            "-shortest",  # Match shortest stream
            *self.config.to_ffmpeg_args(),
            temp_output
        ]
        
        process = await asyncio.create_subprocess_exec(
            *cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        
        stdout, stderr = await process.communicate()
        
        if process.returncode != 0:
            error_msg = stderr.decode() if stderr else "Unknown FFmpeg error"
            raise VideoCompositionError(f"Video-audio combination failed: {error_msg}")
        
        return temp_output
    
    async def _add_subtitles(self, video_path: str, timeline: VideoTimeline, output_path: str) -> str:
        """Add subtitles to video if available."""
        # Check if we have subtitle content
        subtitle_segments = [s for s in timeline.segments if s.subtitle_text]
        
        if not subtitle_segments:
            # No subtitles, just copy video to final output
            import shutil
            shutil.copy2(video_path, output_path)
            return output_path
        
        # Create SRT file from timeline
        srt_path = self._create_temp_file("subtitles.srt")
        await self._create_srt_from_timeline(timeline, srt_path)
        
        # Add subtitles to video
        cmd = [
            "ffmpeg", "-y",
            "-i", video_path,
            "-vf", f"subtitles={srt_path}",
            "-c:a", "copy",
            output_path
        ]
        
        process = await asyncio.create_subprocess_exec(
            *cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        
        stdout, stderr = await process.communicate()
        
        if process.returncode != 0:
            # If subtitle addition fails, fallback to video without subtitles
            print("⚠️ 字幕添加失败，使用无字幕版本")
            import shutil
            shutil.copy2(video_path, output_path)
        
        return output_path
    
    async def _create_srt_from_timeline(self, timeline: VideoTimeline, srt_path: str) -> None:
        """Create SRT subtitle file from timeline."""
        from ..script.subtitle_generator import SubtitleEntry
        from datetime import timedelta
        
        subtitle_entries = []
        subtitle_index = 1
        
        for segment in timeline.segments:
            if segment.subtitle_text and segment.segment_type == SegmentType.SLIDE:
                entry = SubtitleEntry(
                    index=subtitle_index,
                    start_time=timedelta(seconds=segment.start_time),
                    end_time=timedelta(seconds=segment.end_time),
                    text=segment.subtitle_text[:100]  # Limit text length
                )
                subtitle_entries.append(entry)
                subtitle_index += 1
        
        # Write SRT file
        with open(srt_path, 'w', encoding='utf-8') as f:
            for entry in subtitle_entries:
                f.write(entry.to_srt_format())
                f.write('\n')
    
    async def create_preview_video(self, html_path: str, 
                                 timeline: VideoTimeline,
                                 output_path: str,
                                 max_duration: float = 30.0) -> str:
        """
        Create a short preview video.
        
        Args:
            html_path: HTML presentation path
            timeline: Video timeline
            output_path: Output path for preview
            max_duration: Maximum preview duration in seconds
            
        Returns:
            Path to preview video
        """
        print(f"🎬 创建预览视频 (最长 {max_duration} 秒)...")
        
        # Create shortened timeline for preview
        preview_timeline = VideoTimeline(self.config)
        preview_duration = 0.0
        
        for segment in timeline.segments:
            if preview_duration >= max_duration:
                break
            
            # Adjust segment duration to fit preview
            remaining_time = max_duration - preview_duration
            segment_duration = min(segment.duration, remaining_time)
            
            preview_segment = TimelineSegment(
                segment_id=f"preview_{segment.segment_id}",
                segment_type=segment.segment_type,
                start_time=preview_duration,
                duration=segment_duration,
                slide_number=segment.slide_number,
                audio_path=segment.audio_path,
                subtitle_text=segment.subtitle_text,
                metadata=segment.metadata
            )
            
            preview_timeline.add_segment(preview_segment)
            preview_duration += segment_duration
        
        # Compose preview video
        return await self.compose_video(html_path, preview_timeline, output_path)
    
    def _create_temp_file(self, filename: str) -> str:
        """Create temporary file path."""
        temp_path = os.path.join(tempfile.gettempdir(), filename)
        self._temp_files.append(temp_path)
        return temp_path
    
    async def _cleanup_temp_files(self) -> None:
        """Clean up temporary files."""
        for temp_file in self._temp_files:
            try:
                if os.path.exists(temp_file):
                    os.remove(temp_file)
            except Exception:
                pass
        self._temp_files.clear()
    
    def get_composer_info(self) -> Dict[str, Any]:
        """Get information about composer capabilities."""
        return {
            "video_config": {
                "resolution": f"{self.config.width}x{self.config.height}",
                "fps": self.config.fps,
                "codec": self.config.video_codec,
                "format": self.config.format
            },
            "supported_features": [
                "audio_sync",
                "subtitle_embedding",
                "background_music",
                "timeline_composition",
                "preview_generation"
            ],
            "capture_info": self.html_capture.get_capture_info()
        }


class VideoCompositionError(MD2PPTError):
    """Raised when video composition fails."""
    pass